// feat/banks-computations.h

// Copyright 2009-2011  Phonexia s.r.o.;  Microsoft Corporation
//                2016  Johns Hopkins University (author: Daniel Povey)
//                2016  CereProc Ltd. (author: Blaise Potard)

// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.

#ifndef KALDI_FEAT_BANKS_COMPUTATIONS_H_
#define KALDI_FEAT_BANKS_COMPUTATIONS_H_

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <complex>
#include <utility>
#include <vector>
#include <string>

#include "base/kaldi-common.h"
#include "util/common-utils.h"
#include "matrix/matrix-lib.h"

namespace kaldi {
/// @addtogroup  feat FeatureExtraction
/// @{

struct FrameExtractionOptions;  // defined in feature-window.h

// TODO(BP): add support for different type of bank interpolation
// e.g. rectangular and hanning
struct FrequencyBanksOptions {
  int32 num_bins;  // e.g. 25; number of triangular bins
  BaseFloat low_freq;  // e.g. 20; lower frequency cutoff
  BaseFloat high_freq;  // an upper frequency cutoff; 0 -> no cutoff, negative
                        // ->added to the Nyquist frequency to get the cutoff.
  BaseFloat vtln_low;  // vtln lower cutoff of warping function.
  BaseFloat vtln_high;  // vtln upper cutoff of warping function: if negative,
                        //  added to the Nyquist frequency to get the cutoff.
  bool debug_banks;
  // htk_mode is a "hidden" config, it does not show up on command line.
  // Enables more exact compatibibility with HTK, for testing purposes.  Affects
  // mel-energy flooring and reproduces a bug in HTK.
  bool htk_mode;
  std::string scale_type;

  explicit FrequencyBanksOptions(int num_bins = 25)
      : num_bins(num_bins), low_freq(20), high_freq(0), vtln_low(100),
        vtln_high(-500), debug_banks(false),
        htk_mode(false), scale_type("mel") {}

  void Register(OptionsItf *opts) {
    opts->Register("num-mel-bins", &num_bins,
                   "Number of triangular mel-frequency bins");
    opts->Register("low-freq", &low_freq,
                   "Low cutoff frequency for banks bins");
    opts->Register("high-freq", &high_freq,
                   "High cutoff frequency for banks bins (if < 0,"
                   " offset from Nyquist)");
    opts->Register("vtln-low", &vtln_low,
                   "Low inflection point in piecewise linear VTLN warping"
                   " function");
    opts->Register("vtln-high", &vtln_high,
                   "High inflection point in piecewise linear VTLN warping"
                   " function (if negative, offset from high-mel-freq");
    opts->Register("debug-banks", &debug_banks,
                   "Print out debugging information for banks bin computation");
    opts->Register("scale-type", &scale_type, "Scale to use for bin computation"
                   " (\"mel\"|\"bark\"|\"linear\")");
  }
};


class FrequencyBanks {
 public:
  static inline BaseFloat Scale(BaseFloat freq, std::string scale_type) {
    if (scale_type == "bark") {
      return 26.81f / (1960.0f / freq + 1.0) - 0.53;
    } else if (scale_type == "mel") {
      return 1127.0f * logf (1.0f + freq / 700.0f);
    } else if (scale_type == "linear") {
      return freq;
    } else {
      KALDI_ERR << "Invalid scale type " << scale_type;
      return 0.0;
    }
  }

  static inline BaseFloat InverseScale(BaseFloat freq, std::string scale_type) {
    if (scale_type == "bark") {
      return  1960.0f / (26.81f / (freq + 0.53f) - 1.0f);
    } else if (scale_type == "mel") {
      return 700.0f * (expf (freq / 1127.0f) - 1.0f);
    } else if (scale_type == "linear") {
      return freq;
    } else {
      KALDI_ERR << "Invalid scale type " << scale_type;
      return 0.0;
    }
  }


  static BaseFloat VtlnWarpFreqLinear(BaseFloat vtln_low_cutoff,
                                      BaseFloat vtln_high_cutoff,
                                      BaseFloat low_freq,
                                      BaseFloat high_freq,
                                      BaseFloat vtln_warp_factor,
                                      BaseFloat freq);

  static BaseFloat VtlnWarpFreq(BaseFloat vtln_low_cutoff,
                                BaseFloat vtln_high_cutoff,
                                BaseFloat low_freq,
                                BaseFloat high_freq,
                                BaseFloat vtln_warp_factor,
                                BaseFloat freq,
                                std::string scale_type);


  FrequencyBanks(const FrequencyBanksOptions &opts,
                 const FrameExtractionOptions &frame_opts,
                 BaseFloat vtln_warp_factor);

  /// Compute Banks energies (note: not log energies).
  /// At input, "fft_energies" contains the FFT energies (not log).
  void Compute(const VectorBase<BaseFloat> &fft_energies,
               VectorBase<BaseFloat> *banks_energies_out) const;

  int32 NumBins() const { return bins_.size(); }

  // returns vector of central freq of each bin; needed by plp code.
  const Vector<BaseFloat> &GetCenterFreqs() const { return center_freqs_; }

  // Copy constructor
  FrequencyBanks(const FrequencyBanks &other);
 private:
  // Disallow assignment
  FrequencyBanks &operator = (const FrequencyBanks &other);

  // center frequencies of bins, numbered from 0 ... num_bins-1.
  // Needed by GetCenterFreqs().
  Vector<BaseFloat> center_freqs_;

  // the "bins_" vector is a vector, one for each bin, of a pair:
  // (the first nonzero fft-bin), (the vector of weights).
  std::vector<std::pair<int32, Vector<BaseFloat> > > bins_;

  bool debug_;
  bool htk_mode_;
};

/// @} End of "addtogroup feat"
}  // namespace kaldi

#endif  // KALDI_FEAT_BANKS_COMPUTATIONS_H_
